//----------------------------------------------------------------
//module name : yhz_instruction_fetch
//engineer : yhz
//date : 2021.07.26
//----------------------------------------------------------------
`include "yhz_defines.v"
module yhz_instruction_fetch (
    input  wire        i_clk             ,
    input  wire        i_rst             ,
    input  wire        i_pipeline_pulse  ,
    output wire        o_hand_shake_flag ,
    //trap
    input  wire        i_trap            ,
    input  wire        i_mret            ,
    input  wire [63:0] i_mtvec_addr      ,
    input  wire [63:0] i_mepc_addr       ,
    //jump
    input  wire        i_jal_en          ,
    input  wire        i_jalr_en         ,
    input  wire        i_branch_en       ,
    input  wire [63:0] i_pcj_addr        ,
    //to_AXI
    input  wire [63:0] i_r_inst_data     ,
    input  wire        i_r_inst_ready    ,
    output wire        o_r_inst_valid    ,
    output wire [63:0] o_r_inst_addr     ,
    //to_instruction_decode
    output wire [63:0] o_pc_addr         ,
    output wire [31:0] o_instruction      
);
//----------------------------------------------------------------
//register & wire
//----------------------------------------------------------------
    wire        r_inst_valid    = i_pipeline_pulse ? 1'b0 : 1'b1 ;
    wire        hand_shake_flag = r_inst_valid & i_r_inst_ready  ;
    
    wire [63:0] pc_addr_t       = hand_shake_flag ? r_inst_addr : pc_addr ; 
    wire [31:0] instruction     = hand_shake_flag ? r_inst_addr[2] ? i_r_inst_data[63:32] : i_r_inst_data[31:0] : 32'd0 ;

    reg  [63:0] r_inst_addr_t ;
    reg  [63:0] r_inst_addr   ;
    reg  [63:0] pc_addr       ;
//----------------------------------------------------------------
//logic
//----------------------------------------------------------------
    //r_inst_addr_t
    always @(*) begin
        if(i_rst) begin
            r_inst_addr_t = 64'h00000000_80000000 ;
        end
        else if(hand_shake_flag) begin
            case({i_trap,i_mret,i_branch_en,i_jal_en,i_jalr_en})
                5'b10000 : r_inst_addr_t = i_mtvec_addr ;
                5'b01000 : r_inst_addr_t = i_mepc_addr ;
                5'b00100 : r_inst_addr_t = r_inst_addr + i_pcj_addr ;
                5'b00010 : r_inst_addr_t = i_pcj_addr[63]? (r_inst_addr - (~i_pcj_addr + 1'b1)) : (r_inst_addr + i_pcj_addr) ;
                5'b00001 : r_inst_addr_t = i_pcj_addr ;
                default   : r_inst_addr_t = r_inst_addr + 64'd4 ;
            endcase
        end
        else begin
            r_inst_addr_t = r_inst_addr ;
        end
    end
    //r_inst_addr
    always @(posedge i_clk) begin
        if(i_rst) begin
            r_inst_addr <= 64'h00000000_80000000 ;
        end
        else begin
            r_inst_addr <= r_inst_addr_t ;
        end
    end
    //pc_addr
    always @(posedge i_clk) begin
        if(i_rst) begin
            pc_addr <= 64'h00000000_80000000 ;
        end
        else begin
            pc_addr <= pc_addr_t ;
        end
    end
//----------------------------------------------------------------
//output
//----------------------------------------------------------------
    assign o_hand_shake_flag = hand_shake_flag ;
    assign o_r_inst_valid    = r_inst_valid    ;
    assign o_r_inst_addr     = r_inst_addr     ;
    assign o_pc_addr         = pc_addr_t       ;
    assign o_instruction     = instruction     ;
//----------------------------------------------------------------
endmodule
//----------------------------------------------------------------
